/**
 * Create in 2012-4-18.
 */
package kit4j.view.cs.awt;

import java.awt.GraphicsConfiguration;
import java.awt.Window;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.naming.OperationNotSupportedException;
import javax.swing.SwingUtilities;

/**
 * 窗体不透明度分步变化操作实现类。
 * 
 * @author MiniKnife
 * 
 */
public class DynamicWindowStepOpaquer extends AbstractDynamicComponentStepOperator {

	/**
	 * {@link com.sun.awt.AWTUtilities}工具类的Class对象。
	 */
	@SuppressWarnings("restriction")
	private static Class<?> awtUtilitiesClass;

	/**
	 * 验证是否支持窗体不透明度变化的Method对象。
	 */
	private static Method checkSupportMethod;

	/**
	 * 调整窗体不透明度的Method对象。
	 */
	private static Method opaqueMethod;

	/**
	 * 绑定的窗体对象。
	 */
	private Window window;

	/**
	 * 下一步将要调整到的不透明度。
	 */
	private float nextOpacity;

	/**
	 * 当前给窗体设置的不透明度。
	 */
	private float currentOpacity;

	/**
	 * 窗体的初始不透明度。
	 */
	private float initialOpacity;

	/**
	 * 窗体不透明度的变化增量。
	 */
	private float opacityIncrement;

	/**
	 * 创建一个窗体不透明度分步调整操作者对象。
	 * 
	 * @param window
	 *            要操作的窗体
	 * @param initialOpacity
	 *            窗体的初始不透明度
	 * @param targetOpacity
	 *            窗体的目标不透明度
	 * @param opacityIncrement
	 *            窗体不透明度的变化增量
	 * @throws OperationNotSupportedException
	 *             如果当前绑定的窗体对象不支持不透明度变化，则抛出此异常。
	 */
	public DynamicWindowStepOpaquer(Window window, float initialOpacity, float targetOpacity, float opacityIncrement)
			throws OperationNotSupportedException {
		super((long) Math.abs((targetOpacity - initialOpacity) * 10000000));

		try {
			if (isWindowOpacityControlSupported(window)) {
				checkParameter(initialOpacity, targetOpacity, opacityIncrement);
				initOpaqueMethod();

				this.window = window;
				this.nextOpacity = this.currentOpacity = this.initialOpacity = initialOpacity;
				this.opacityIncrement = opacityIncrement;
			} else {
				throw new OperationNotSupportedException();
			}
		} catch (Exception e) {
			throw new OperationNotSupportedException(e.getLocalizedMessage());
		}
	}
	
	/**
	 * 判断指定的Window对象是否支持不透明度变化操作。
	 * 
	 * @param window
	 *            Window对象
	 * @return 如果指定的Window对象支持不透明度变化操作，则返回true，否则返回false。
	 */
	public static boolean isWindowOpacityControlSupported(Window window) {
		try {
			return (Boolean) getCheckSupportMethod().invoke(null, window.getGraphicsConfiguration());
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * @return 验证是否支持窗体不透明度变化的Method对象。
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @see java.lang.Class#getMethod(String, Class...)
	 */
	private static Method getCheckSupportMethod() throws SecurityException, NoSuchMethodException,
			ClassNotFoundException {
		if (checkSupportMethod == null) {
			checkSupportMethod = getAWTUtilitiesClass().getMethod("isTranslucencyCapable", GraphicsConfiguration.class);
		}

		return checkSupportMethod;
	}

	/**
	 * @return {@link com.sun.awt.AWTUtilities}工具类的Class对象。
	 * @throws ClassNotFoundException
	 *             如果当前运行环境中不存在该类型
	 */
	@SuppressWarnings("restriction")
	private static Class<?> getAWTUtilitiesClass() throws ClassNotFoundException {
		if (awtUtilitiesClass == null) {
			awtUtilitiesClass = Class.forName("com.sun.awt.AWTUtilities");
		}

		return awtUtilitiesClass;
	}

	/**
	 * 初始化调整窗体不透明度的Method对象。
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	private static void initOpaqueMethod() throws SecurityException, NoSuchMethodException, ClassNotFoundException {
		if (opaqueMethod == null) {
			opaqueMethod = getAWTUtilitiesClass().getMethod("setWindowOpacity", Window.class, Float.TYPE);
		}
	}

	/**
	 * @return 调整窗体不透明度的Method对象。
	 */
	private static Method getOpaqueMethod() {
		return opaqueMethod;
	}

	/**
	 * 验证参数。
	 * @param initialOpacity
	 *            窗体的初始不透明度
	 * @param targetOpacity
	 *            窗体的目标不透明度
	 * @param opacityIncrement
	 *            窗体不透明度的变化增量
	 * @throws IllegalArgumentException 如果存在一个不合法的参数，则抛出此异常。
	 */
	private static void checkParameter(float initialOpacity, float targetOpacity, float opacityIncrement)
			throws IllegalArgumentException {
		if (initialOpacity < 0 || initialOpacity > 1.0F) {
			throw new IllegalArgumentException("The value of initialOpacity should be in the range [0.0f .. 1.0f].");
		}
		if (targetOpacity < 0 || targetOpacity > 1.0F) {
			throw new IllegalArgumentException("The value of targetOpacity should be in the range [0.0f .. 1.0f].");
		}
		if (opacityIncrement < -1.0F || opacityIncrement > 1.0F || opacityIncrement == 0F) {
			throw new IllegalArgumentException("The value of opacityIncrement should be in the range [-1.0f .. 0f) or (0f .. 1.0f].");
		}
	}

	/**
	 * @see kit4j.common.task.StepTask.StepTaskCallback#execute()
	 */
	@Override
	public void execute() {
		try {
			SwingUtilities.invokeAndWait(new Runnable() {

				@Override
				public void run() {
					try {
						getOpaqueMethod().invoke(null, window, nextOpacity);

						currentOpacity = nextOpacity;
						nextOpacity = nextOpacity + opacityIncrement;

						if (nextOpacity > 1.0F) {
							nextOpacity = 1.0F;
						} else if (nextOpacity < 0F) {
							nextOpacity = 0F;
						}
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}

			});
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @see kit4j.common.task.StepTask.StepTaskCallback#getCurrentTaskProgressRate()
	 */
	@Override
	public long getCurrentTaskProgressRate() {
		return (long) Math.abs((currentOpacity - initialOpacity) * 10000000);
	}

}
